package com.wrupple.muba.common.shared.process.impl;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.inject.Inject;
import com.wrupple.muba.common.client.activity.ActivityProcess;
import com.wrupple.muba.common.client.activity.SimpleActivityTransition;
import com.wrupple.muba.common.client.application.DataCallback;
import com.wrupple.muba.common.shared.Process;
import com.wrupple.muba.common.shared.ServiceBus;
import com.wrupple.muba.common.shared.State.Context;
import com.wrupple.muba.common.shared.State.DesktopManager;
import com.wrupple.muba.common.shared.State.ProcessManager;
import com.wrupple.muba.common.shared.State.StorageManager;
import com.wrupple.muba.common.shared.StateTransition;
import com.wrupple.muba.common.shared.event.ContextSwitchEvent;
import com.wrupple.muba.common.shared.event.ProcessSwitchEvent;
import com.wrupple.muba.desktop.domain.HumanActivityContext;

public class SequentialProcessManager implements ProcessManager {

	public static class SetApplicationStateAndContext extends DataCallback<ActivityProcess> {
		ProcessManager webapp;
		AcceptsOneWidget panel;
		EventBus eventBus;
		private String localizedName;

		public SetApplicationStateAndContext(ProcessManager webapp, AcceptsOneWidget panel, EventBus eventBus,
				String localizedName) {
			super();
			this.webapp = webapp;
			this.panel = panel;
			this.eventBus = eventBus;
			this.localizedName = localizedName;
		}

		@Override
		public void execute() {
			webapp.contextSwitch(result, localizedName, panel, eventBus);
		}

	}

	class NestedProcessCallback<O> extends DataCallback<O>{

		private boolean called;
		StateTransition<O> callback;
		EventBus bus;
		Process<?, ?> oldProcess ;

		public NestedProcessCallback(StateTransition<O> callback, EventBus bus,
				Process<?, ?> oldProcess) {
			super();
			called = false;
			this.callback = callback;
			this.bus = bus;
			this.oldProcess = oldProcess;
		}


		@Override
		public void execute() {
			if (!called) {
				called = true;
				// restore previous process
				oldProcess.restore(context);
				setCurrentProcess(null, oldProcess, false, bus);

				callback.setResult(result);
				callback.execute();
			}
		}
		
	}

	/*
	 * SERVICES
	 */

	protected ServiceBus serviceBus;
	protected DesktopManager desktopManager;
	protected StorageManager storageManager;
	/*
	 * STATE
	 */
	private Context context;
	private Process<?, ?> current;
	
	

	@Inject
	public SequentialProcessManager(StorageManager storageManager, DesktopManager desktopManager,ServiceBus serviceBus) {
		super();
		this.storageManager = storageManager;
		this.desktopManager = desktopManager;
		this.serviceBus = serviceBus;
	}

	@Override
	public void contextSwitch(ActivityProcess activityProcess, String localizedName,
			AcceptsOneWidget container, EventBus bus) {
		AcceptsOneWidget output = desktopManager.getOutputFeature();
		context = new HumanActivityContext(output,  this, serviceBus, desktopManager, storageManager);
		
		setCurrentProcess(localizedName, activityProcess, false, bus);
		bus.fireEvent(new ContextSwitchEvent(context,activityProcess));
		activityProcess.start(desktopManager.getWhere(), new SimpleActivityTransition(desktopManager), bus);
	}

	@Override
	public <I, O> void processSwitch(Process<I, O> process, String localizedName, I input,
			final StateTransition<O> callback, final EventBus bus) {
		final Process<?, ?> oldProcess = this.current;
		
		oldProcess.suspend();
		
		setCurrentProcess(localizedName, process, true, bus);
		
		StateTransition<O> cbk = new NestedProcessCallback<O>(callback, bus, oldProcess);
		bus.fireEvent(new ProcessSwitchEvent(oldProcess,process));
		process.start(input, cbk , bus);

	}
	

	private void setCurrentProcess(String string, Process<?, ?> newProcess, boolean pushPrevious, EventBus eventBus) {
		this.current = newProcess;
		newProcess.setContext(context);
	}


}
